#pragma once
#include<iostream>
#include<sys/select.h>
#include"Socket.hpp"
#include"InetAddr.hpp"
#include"log.hpp"


class SelectServer
{
public:
	static const int gnum = sizeof(fd_set)*8;
	static const int gdefaultfd = -1;
	SelectServer(uint16_t port):_port(port),_listensock(make_unique<TcpSocket>())
	{
		_listensock->BuildListenSocket(port);
	};
	void InitServer()
	{
		fd_array[0] = _listensock->Sockfd();
		for(int i =1;i<gnum;i++)
		{
			fd_array[i] = gdefaultfd;
		};
	};
	void Accepter()
	{
		InetAddr addr;
		int sockfd = _listensock->Accepter(&addr);

		LOG(DEBUG, "get a new link, client info %s:%d\n", addr.Ip().c_str(), addr.Port());
		if(sockfd>0)
		{
			int i =0;
			for(;i<gnum;i++)
			{
				if(fd_array[i]==gdefaultfd)
				{
					fd_array[i]=sockfd;
					LOG(INFO, "add %d to fd_array success!\n", sockfd);
					break;
				};
			}
			if(i==gnum)
			{
				LOG(ERROR,"fd_array full!\n");
				close(sockfd);
			};
		};
	};
	void HandlerIO(int i )
	{
		char buffer[1024];
		ssize_t n = recv(fd_array[i],buffer,sizeof(buffer)-1,0);
		if(n>0)
		{
			buffer[n] = 0;
			cout<<fd_array[i]<<"said:"<<buffer<<endl;

		}
		else if(n==0)
		{
			LOG(INFO,"%d: quit\n",fd_array[i]);
			fd_array[i] =gdefaultfd;
			close(fd_array[i]);
		}
		else
		{
			LOG(ERROR,"%d: error\n",fd_array[i]);
			fd_array[i] =gdefaultfd;
			close(fd_array[i]);
		};
	};
	void HandlerEvent(const fd_set& rfds)
	{
		for(int i =0;i<gnum;i++)
		{
			if(fd_array[i]==gdefaultfd)
				continue;
			if(FD_ISSET(fd_array[i],&rfds))
			{
				if(fd_array[i] ==_listensock->Sockfd())
					Accepter();
				else
					HandlerIO(i);
			}
		};

	};
	void PrintDebug()
	{
		cout<<"fd_array:";
		for(int i =0;i<gnum;i++)
			if(fd_array[i]!=gdefaultfd)
				cout<<" "<<fd_array[i];
		cout<<endl;
	};
	void Loop()
	{
		while(true)
		{
			//为调用select函数需要的参数做准备
			fd_set rfds;
			FD_ZERO(&rfds);//这一步必须在循环内，因为调用select的时候会修改rfds内部的值，需要每次使用都重置
			int max_fd = gdefaultfd;
			for(int i =0;i<gnum;i++)
			{
				if(fd_array[i]==gdefaultfd)
					continue;
				FD_SET(fd_array[i],&rfds);
				if(fd_array[i]>max_fd)
					max_fd = fd_array[i];
			};
			struct timeval timeout = {30,0};
			//调用select
			int n = select(max_fd+1,&rfds,nullptr,nullptr,&timeout);
			if(n>0)
			{
				LOG(INFO,"exist event ready! \n");
				HandlerEvent(rfds);
				PrintDebug();

			}
			else if(n==0)
			{
				LOG(INFO,"time out\n");
			}
			else
			{
				LOG(ERROR,"select error\n");
			};
		};
	};
		




private:
	uint16_t _port;
	unique_ptr<Socket> _listensock;
	int fd_array[gnum];
};
